/*
 Taylor Seale, Justin Barlett, Ka Hin Lee
 Computer Networks - Project 2
 Multithreaded Thermometer Server
 
 Server portion of Project 2

 Server receives a packet (or two) from the client and appends that information to
 a temp_log file for the client host if the information is new

 Supports multithreading, multiple clients may connect at once
*/

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>

// structure to handle data sent from client
struct client_data
{
	char host_name[32];
	int num_therms;
	int sensor_num;
	double sensor_data;
	double low_val;
	double high_val;
	char timestamp[32];
	int action;
};

// thread function, interprets the packet and writes to file logs accordingly
// receives the socket identifier as an argument
void* read_data(void* param)
{
	// store socket information for thread connection locally
	int *fdptr=param;
	int client_local= *fdptr; 
	free (fdptr);
	int is_two_sensors=0; // determine whether one or 2 sensors have sent data
	// structures for up to 2 thermometers
	struct client_data data1;
	struct client_data data2;
	bzero(&data1,sizeof(data1));
	bzero(&data2,sizeof(data2));
	// reads data from client for the first structure
	if (read(client_local,(struct client_data*)&data1,sizeof(data1))<0)
	{
		printf("Error reading data from client...\n");
	}
	
	// determines if there have been two pieces of sensor data sent
	if (data1.num_therms==2)
	{
		// alter the flag and read in the second thermometer reading
		is_two_sensors=1;
		if (read(client_local,(struct client_data*)&data2,sizeof(data2))<0)
		{
			printf("Error reading data from client...\n");
		}		
	}
	
	// sometimes the host exists early (due to errors), resulting in no packet being sent
	int packet_sent=1;
	if (strcmp(data1.host_name,"")==0)
	{
		packet_sent=0;
	}

	// if we have a new reading we need to write it to the file
	// make sure that one of the data readings are new and that a packet was actually sent
	if ((data1.action==0 || data2.action==0) && packet_sent)
	{
		// set up the string to append to the file
		char write_to_file[32];
		if (is_two_sensors)
		{
			sprintf(write_to_file,"%s %.2f %.2f\n",data1.timestamp,data1.sensor_data,data2.sensor_data);
		}
		else
		{
			sprintf(write_to_file,"%s %.2f\n",data1.timestamp,data1.sensor_data);
		}

		// create the file path for the temp_log
		char filepath[512];
		strcat(filepath,"/var/log/therm/temp_logs/g06_");
		// add the current year & month
		time_t curr_time;
		curr_time=time(NULL);
		struct tm *tm;
		tm=localtime(&curr_time);
		char year_month[128];
		sprintf(year_month,"%04d_%02d_",tm->tm_year+1900,tm->tm_mon+1);
		strcat(filepath,year_month);
		// add the hostname of the client
		strcat(filepath,data1.host_name);
		// append to the file
		FILE* temp_log;
		temp_log=fopen(filepath, "a");
		fprintf(temp_log, write_to_file);
		fclose(temp_log);
	}
	close(client_local); // close the socket for this thread
	pthread_exit(NULL); // exit this thread
	return 0;
}


int main (int argc, char** argv)
{
	// make some global variables (so each thread can use them
	int listenfd; // listening socket
	
	struct sockaddr_in servaddr; // structures for client and server addresses

	
	// create a socket to listen on
	if ((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
	{
		printf("Failed to create a socket...\n");
		exit(1);
	}
	
	// set up network stuff
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr.sin_port=htons(9767);
	
	// bind to socket
	if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
	{
		printf("Failed to bind...\n");
		exit(1);
	}
	
	// set server up to listen for clients
	if (listen(listenfd,1024)<0)
	{
		printf("Connection failed...\n");
		exit(1);
	}
	
	// handle connections from clients
	while (1)
	{
		// moved into while loop
		unsigned int connfd;
		socklen_t clilen;
		struct sockaddr_in cliaddr;
		
		pthread_t child_pid;
		clilen=sizeof(cliaddr);
		// create client socket
		if((connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen))<0)
		{
			printf("Failed to create a socket...\n");
			exit(1);
		}
		int *param = malloc (sizeof(*param));
		*param = connfd;
		// create a new thread for the accepted connection
		pthread_create(&child_pid, NULL, read_data, param);
	}
	close(listenfd);
	pthread_exit(NULL);
	return 0;
}